#include <stdlib.h>
#include <memory.h>
#include <string.h>
#include <stdio.h>
#include "phpserialize.h"

static int find_idx_of_string(char *buf, int len, char ch)
{
    int i = 0;
    for (; (i < len) && (buf[i] != ch); i++);
    return (i >= len) ? -1 : i; 
}

static int dlib_php_decode(dlib_php_handle handle, char *buf, int len)
{
    dlib_php_element_t *element = NULL;
    dlib_php_element_t *sub_elements = NULL;
    int tail = -1;
    char num[32] = {0};
    int colon = -1;
    int flag = 0;
    int count = 0;
    int idx = 0;
    int inc = 0;

    switch (buf[0]) {
        case 'b':
        case 'B':
            tail = find_idx_of_string(buf, len, ';');
            if ((buf[1] != ':') || (tail == -1)) {
                return -1;
            }

            memcpy(num, &buf[2], tail - 2);
            num[tail-2] = 0;

            if (dlib_php_set_bool(handle, atoi(num)) == -1) {
                return -1;
            }
            break;
        case 'i':
        case 'I':
            tail = find_idx_of_string(buf, len, ';');
            if ((buf[1] != ':') || (tail == -1)) {
                return -1;
            }

            memcpy(num, &buf[2], tail - 2);
            num[tail-2] = 0;

            if (dlib_php_set_integer(handle, atoi(num)) == -1) {
                return -1;
            }
            break;
        case 'd':
        case 'D':
            tail = find_idx_of_string(buf, len, ';');
            if ((buf[1] != ':') || (tail == -1)) {
                return -1;
            }

            memcpy(num, &buf[2], tail - 2);
            num[tail-2] = 0;

            if (dlib_php_set_double(handle, atof(num)) == -1) {
                return -1;
            }
            break;
        case 's':
        case 'S':
            colon = find_idx_of_string(&buf[2], len - 2, ':');
            if ((buf[1] != ':') || (colon == -1)) {
                return -1;
            }

            memcpy(num, &buf[2], colon);
            num[colon] = 0;

            tail = 5 + atoi(num) + colon;
            if ((buf[3+colon] != '\"') || (buf[tail-1] != '\"') || (buf[tail] != ';')) {
                return -1;
            }

            if (dlib_php_set_string(handle, &buf[4+colon], tail - colon - 5) == -1) {
                return -1;
            }
            break;
        case 'n':
        case 'N':
            tail = 1;
            if (buf[tail] != ';') {
                return -1;
            }

            if (dlib_php_set_null(handle) == -1) {
                return -1;
            }
            break;
        case 'a':
        case 'A':
            colon = find_idx_of_string(&buf[2], len - 2, ':');
            if ((buf[1] != ':') || (colon == -1) || (buf[colon+3] != '{')) {
                return -1;
            }

            for (tail = colon + 4, flag = 1; tail < len; tail++) {
                if (buf[tail] == '{') {
                    flag++;
                }
                else if (buf[tail] == '}') {
                    flag--;
                }

                if (flag == 0) {
                    break;
                }
            }

            if (tail >= len) {
                return -1;
            }

            memcpy(num, &buf[2], colon);
            num[colon] = 0;

            count = atoi(num);
            if (count <= 0) {
                return -1;
            }

            if (dlib_php_set_array(handle, count) == -1) {
                return -1;
            }

            inc = 0;
            sub_elements = handle->a.elements;

            for (colon += 4, idx = 0; idx < (count * 2); colon += inc, idx++) {
                inc = dlib_php_decode((dlib_php_handle)&sub_elements[idx], &buf[colon], len - colon);
                if (inc == -1) {
                    dfree(sub_elements);
                    return -1;
                }
            }

            break;
        case 'o':
        case 'O':
            colon = find_idx_of_string(&buf[2], len - 2, ':');
            if ((buf[1] != ':') || (colon == -1)) {
                return -1;
            }

            memcpy(num, &buf[2], colon);
            num[colon] = 0;

            count = atoi(num);
            if ((count <= 0) || (buf[colon+3] != '\"') ||
                (buf[colon+count+4] != '\"') || (buf[colon+count+5] != ':')) {
                    return -1;
            }

            element = dlib_php_get_element(handle);
            if (element != NULL) {
                element->o.name = (char*)dmalloc(sizeof(char) * (count + 1));
                if (element->o.name == NULL) {
                    return -1;
                }

                memcpy(element->o.name, &buf[colon+4], count);
                element->o.name[count] = 0;
                element->t = DLIB_PHP_DATATYPE_OBJECT;
            }

            tail = colon + count + 6;
            colon = find_idx_of_string(&buf[tail], len - tail, ':');
            if (colon == -1) {
                if (element != NULL) {
                    dfree(element->o.name);
                    return -1;
                }
            }

            memcpy(num, &buf[tail], colon);
            num[colon] = 0;

            count = atoi(num);
            if ((count <= 0) || (buf[tail+colon] != ':') || (buf[tail+colon+1] != '{')) {
                if (element != NULL) {
                    dfree(element->o.name);
                    return -1;
                }
            }

            colon += tail;
            for (tail = colon + 2, flag = 1; tail < len; tail++) {
                if (buf[tail] == '{') {
                    flag++;
                }
                else if (buf[tail] == '}') {
                    flag--;
                }

                if (flag == 0) {
                    break;
                }
            }

            if (tail >= len) {
                if (element != NULL) {
                    dfree(element->o.name);
                    return -1;
                }
            }

            if (element != NULL) {
                sub_elements = (dlib_php_element_t*)dmalloc(sizeof(dlib_php_element_t) * count * 2);
                if (sub_elements == NULL) {
                    dfree(element->o.name);
                    return -1;
                }

                memset(sub_elements, 0, sizeof(dlib_php_element_t) * count * 2);
                for (colon += 2, idx = 0, inc = 0; idx < (count * 2); colon += inc, idx++) {
                    inc = dlib_php_decode((dlib_php_handle)&sub_elements[idx], &buf[colon], len - colon);
                    if (inc == -1) {
                        dfree(sub_elements);
                        dfree(element->o.name);
                        return -1;
                    }
                }

                element->o.count = count;
                element->o.members = sub_elements;
            }

            break;
    }

    return tail + 1;
}

static dlib_php_handle dlib_php_unserialize_internal(char *buf, int len)
{
    dlib_php_handle handle = NULL;

    handle = (dlib_php_handle)dmalloc(sizeof(dlib_php_element_t));
    if (handle == NULL) {
        return NULL;
    }

    memset(handle, 0, sizeof(dlib_php_element_t));
    if (dlib_php_decode(handle, buf, len) == -1) {
        dfree(handle);
        return NULL;
    }

    return handle; 
}

dlib_php_handle dlib_php_unserialize(char *buf, int len)
{
    if ((buf != NULL) || (len > 0)) {
        return dlib_php_unserialize_internal(buf, len);
    }
    return DLIB_PHP_INVALID_HANDLE;
}

static int dlib_php_encode(dlib_php_handle handle, char *buf, int *len)
{
    dlib_php_element_t *element = dlib_php_get_element(handle);
    int tail = 0;
    int res = -1;
    int i = 0;

    switch (element->t) {
        case DLIB_PHP_DATATYPE_BOOL:
            res = snprintf(buf, *len, "b:%d;", element->b);
            if (res != -1) {
                *len -= res;
                tail += res;
            }
            break;
        case DLIB_PHP_DATATYPE_INT:
            res = snprintf(buf, *len, "i:%d;", element->i);
            if (res != -1) {
                *len -= res;
                tail += res;
            }
            break;
        case DLIB_PHP_DATATYPE_DOUBLE:
            res = snprintf(buf, *len, "d:%f;", element->d);
            if (res != -1) {
                *len -= res;
                tail += res;
            }
            break;
        case DLIB_PHP_DATATYPE_STRING:
            res = snprintf(buf, *len, "s:%d:\"%s\";", strlen(element->s), element->s);
            if (res != -1) {
                *len -= res;
                tail += res;
            }
            break;
        case DLIB_PHP_DATATYPE_NULL:
            res = snprintf(buf, *len, "N;");
            if (res != -1) {
                *len -= res;
                tail += res;
            }
            break;
        case DLIB_PHP_DATATYPE_ARRAY:
            res = snprintf(buf, *len, "a:%d:{", element->a.count);
            if (res == -1) {
                return -1;
            }

            *len -= res;
            tail += res;

            for (i = 0; i < (element->a.count * 2); i++) {
                res = dlib_php_encode((dlib_php_handle)&element->a.elements[i], &buf[strlen(buf)], len);
                if (res == -1) {
                    return -1;
                }
                *len -= res;
                tail += res;
            }

            res = snprintf(&buf[strlen(buf)], *len, "}");
            if (res == -1) {
                return -1;
            }
            *len -= res;
            tail += res;
            break;
        case DLIB_PHP_DATATYPE_OBJECT:
            res = snprintf(buf, *len, "O:%d:\"%s\":%d:{", strlen(element->o.name), element->o.name, element->o.count);
            if (res == -1) {
                return -1;
            }

            *len -= res;
            tail += res;

            for (i = 0; i < (element->o.count * 2); i++) {
                res = dlib_php_encode((dlib_php_handle)&element->o.members[i], &buf[strlen(buf)], len);
                if (res == -1) {
                    return -1;
                }
                *len -= res;
                tail += res;
            }

            res = snprintf(&buf[strlen(buf)], *len, "}");
            if (res != -1) {
                return -1;
            }
            *len -= res;
            tail += res;
            break;
    }

    return tail;
}

static int dlib_php_serialize_internal(dlib_php_handle handle, char *buf, int *len)
{
    return dlib_php_encode(handle, buf, len);
}

int dlib_php_serialize(dlib_php_handle handle, char *buf, int *len)
{
    if ((handle != NULL) && (buf != NULL) && (len != NULL) && (*len > 0)) {
        return dlib_php_serialize_internal(handle, buf, len);
    }
    return -1;
}

dlib_php_handle dlib_php_create_bool(int b)
{
    dlib_php_element_t *element = NULL;

    element = (dlib_php_element_t*)dmalloc(sizeof(dlib_php_element_t));
    if (element != NULL) {
        element->b = b;
        element->t = DLIB_PHP_DATATYPE_BOOL;
    }
    return (dlib_php_handle)element;
}

dlib_php_handle dlib_php_create_integer(int i)
{
    dlib_php_element_t *element = NULL;

    element = (dlib_php_element_t*)dmalloc(sizeof(dlib_php_element_t));
    if (element != NULL) {
        element->i = i;
        element->t = DLIB_PHP_DATATYPE_INT;
    }
    return (dlib_php_handle)element;
}

dlib_php_handle dlib_php_create_double(double d)
{
    dlib_php_element_t *element = NULL;

    element = (dlib_php_element_t*)dmalloc(sizeof(dlib_php_element_t));
    if (element != NULL) {
        element->d = d;
        element->t = DLIB_PHP_DATATYPE_DOUBLE;
    }
    return (dlib_php_handle)element;
}

dlib_php_handle dlib_php_create_string(char *s, int len)
{
    dlib_php_element_t *element = NULL;

    if ((s == NULL) || (len <= 0)) {
        return NULL;
    }

    element = (dlib_php_element_t*)dmalloc(sizeof(dlib_php_element_t));
    if (element == NULL) {
        return NULL;
    }

    element->s = (char*)dmalloc(sizeof(char) * (len + 1));
    if (element->s == NULL) {
        dfree(element);
        return NULL;
    }

    memcpy(element->s, s, len);
    element->s[len] = 0;
    element->t = DLIB_PHP_DATATYPE_STRING;

    return (dlib_php_handle)element;
}

dlib_php_handle dlib_php_create_null(void)
{
    dlib_php_element_t *element = NULL;

    element = (dlib_php_element_t*)dmalloc(sizeof(dlib_php_element_t));
    if (element != NULL) {
        element->n = 0;
        element->t = DLIB_PHP_DATATYPE_NULL;
    }
    return (dlib_php_handle)element;
}

dlib_php_handle dlib_php_create_array(int count)
{
    dlib_php_element_t *element = NULL;

    if (count <= 0) {
        return NULL;
    }

    element = (dlib_php_element_t*)dmalloc(sizeof(dlib_php_element_t));
    if (element == NULL) {
        return NULL;
    }

    element->a.count = count;
    element->a.elements = (dlib_php_element_t*)dmalloc(sizeof(dlib_php_element_t) * element->a.count * 2);
    if (element->a.elements == NULL) {
        dfree(element);
        return NULL;
    }

    memset(element->a.elements, 0, sizeof(dlib_php_element_t) * element->a.count * 2);
    return (dlib_php_handle)element;
}

dlib_php_handle dlib_php_create_object(char *name, int len, int count)
{
    dlib_php_element_t *element = NULL;

    if ((name == NULL) || (len <= 0) || (count <= 0)) {
        return NULL;
    }

    element = (dlib_php_element_t*)dmalloc(sizeof(dlib_php_element_t));
    if (element == NULL) {
        return NULL;
    }

    element->o.name = (char*)dmalloc(sizeof(char) * (len + 1));
    if (element->o.name == NULL) {
        dfree(element);
        return NULL;
    }

    memcpy(element->o.name, name, len);
    element->o.name[len] = 0;

    element->o.count = count;
    element->o.members = (dlib_php_element_t*)dmalloc(sizeof(dlib_php_element_t) * element->o.count * 2);
    if (element->o.members == NULL) {
        dfree(element->o.name);
        dfree(element);
        return NULL;
    }

    memset(element->o.members, 0, sizeof(dlib_php_element_t) * element->o.count * 2);
    return (dlib_php_handle)element;
}

static void dlib_php_release_handle_internal(dlib_php_handle handle)
{
    dlib_php_element_t *element = dlib_php_get_element(handle);
    dlib_php_element_t *elements = NULL;
    int i = 0;

    switch (element->t) {
        case DLIB_PHP_DATATYPE_STRING:
            dfree(element->s);
            break;
        case DLIB_PHP_DATATYPE_ARRAY:
            elements = element->a.elements;
            for (i = 1; i < (element->a.count * 2); i += 2) {
                dlib_php_release_handle_internal((dlib_php_handle)&elements[i]);
            }
            dfree(elements);
            break;
        case DLIB_PHP_DATATYPE_OBJECT:
            dfree(element->o.name);
            elements = element->o.members;
            for (i = 1; i < (element->o.count * 2); i += 2) {
                dlib_php_release_handle_internal((dlib_php_handle)&elements[i]);
            }
            dfree(elements);
            break;
    }
}

void dlib_php_release_handle(dlib_php_handle handle)
{
    if (handle != NULL) {
        dlib_php_release_handle_internal(handle);
        dfree(handle);
    }
}

int dlib_php_set_bool(dlib_php_handle handle, int b)
{
    if (handle != NULL) {
        dlib_php_release_handle_internal(handle);
        handle->b = b;
        handle->t = DLIB_PHP_DATATYPE_BOOL;
        return 0;
    }
    return -1;
}

int dlib_php_set_integer(dlib_php_handle handle, int i)
{
    if (handle != NULL) {
        dlib_php_release_handle_internal(handle);
        handle->i = i;
        handle->t = DLIB_PHP_DATATYPE_INT;
        return 0;
    }
    return -1;
}

int dlib_php_set_double(dlib_php_handle handle, double d)
{
    if (handle != NULL) {
        dlib_php_release_handle_internal(handle);
        handle->d = d;
        handle->t = DLIB_PHP_DATATYPE_DOUBLE;
        return 0;
    }
    return -1;
}

int dlib_php_set_string(dlib_php_handle handle, char *buf, int len)
{
    char *str = NULL;

    if ((handle == NULL) || (buf == NULL) || (len <= 0)) {
        return -1;
    }

    if (handle->t != DLIB_PHP_DATATYPE_STRING) {
        str = (char*)dmalloc(sizeof(char) * (len + 1));
        if (str == NULL) {
            return -1;
        }

        dlib_php_release_handle_internal(handle);
    }
    else if (strlen(handle->s) < (unsigned int)len) {
        str = (char*)drealloc(handle->s, sizeof(char) * (len + 1));
        if (str == NULL) {
            return -1;
        }
    }

    memcpy(str, buf, len);
    str[len] = 0;

    handle->s = str;
    handle->t = DLIB_PHP_DATATYPE_STRING;

    return 0;
}

int dlib_php_set_null(dlib_php_handle handle)
{
    if (handle != NULL) {
        dlib_php_release_handle_internal(handle);
        handle->n = 0;
        handle->t = DLIB_PHP_DATATYPE_NULL;
        return 0;
    }
    return -1;
}

int dlib_php_set_array(dlib_php_handle handle, int count)
{
    dlib_php_element_t *elements = NULL;

    if ((handle == NULL) || (count <= 0)) {
        return -1;
    }

    if (handle->t != DLIB_PHP_DATATYPE_ARRAY) {
        elements = (dlib_php_element_t*)dmalloc(sizeof(dlib_php_element_t) * count * 2);
        if (elements == NULL) {
            return -1;
        }

        memset(elements, 0, sizeof(dlib_php_element_t) * count * 2);
        dlib_php_release_handle_internal(handle);
    }
    else if (handle->a.count < count) {
        elements = (dlib_php_element_t*)drealloc(handle->a.elements, sizeof(dlib_php_element_t) * count * 2);
        if (elements == NULL) {
            return -1;
        }

        memset(&elements[2*handle->a.count], 0, sizeof(dlib_php_element_t) * 2 * (count - handle->a.count));
    }

    handle->a.count = count;
    handle->a.elements = elements;
    handle->t = DLIB_PHP_DATATYPE_ARRAY;

    return 0;
}

int dlib_php_set_array_element_bool(dlib_php_handle handle, int idx, char *key, int b)
{
    if ((handle == NULL) || (handle->t != DLIB_PHP_DATATYPE_ARRAY) ||
        (idx < 0) || (idx >= handle->a.count)) {
            return -1;
    }

    if ((key == NULL) && (dlib_php_set_integer((dlib_php_handle)&handle->a.elements[idx], idx) == 0) ||
        (key != NULL) && (dlib_php_set_string((dlib_php_handle)&handle->a.elements[idx], key, strlen(key)))) {
            return dlib_php_set_bool((dlib_php_handle)&handle->a.elements[idx+1], b);
    }

    return -1;
}

int dlib_php_set_array_element_integer(dlib_php_handle handle, int idx, char *key, int i)
{
    if ((handle == NULL) || (handle->t != DLIB_PHP_DATATYPE_ARRAY) ||
        (idx < 0) || (idx >= handle->a.count)) {
            return -1;
    }

    if ((key == NULL) && (dlib_php_set_integer((dlib_php_handle)&handle->a.elements[idx], idx) == 0) ||
        (key != NULL) && (dlib_php_set_string((dlib_php_handle)&handle->a.elements[idx], key, strlen(key)))) {
            return dlib_php_set_integer((dlib_php_handle)&handle->a.elements[idx+1], i);
    }

    return -1;
}

int dlib_php_set_array_element_double(dlib_php_handle handle, int idx, char *key, double d)
{
    if ((handle == NULL) || (handle->t != DLIB_PHP_DATATYPE_ARRAY) ||
        (idx < 0) || (idx >= handle->a.count)) {
            return -1;
    }

    if ((key == NULL) && (dlib_php_set_integer((dlib_php_handle)&handle->a.elements[idx], idx) == 0) ||
        (key != NULL) && (dlib_php_set_string((dlib_php_handle)&handle->a.elements[idx], key, strlen(key)))) {
            return dlib_php_set_double((dlib_php_handle)&handle->a.elements[idx+1], d);
    }

    return -1;
}

int dlib_php_set_array_element_string(dlib_php_handle handle, int idx, char *key, char *s, int len)
{
    if ((handle == NULL) || (handle->t != DLIB_PHP_DATATYPE_ARRAY) ||
        (idx < 0) || (idx >= handle->a.count)) {
            return -1;
    }

    if ((key == NULL) && (dlib_php_set_integer((dlib_php_handle)&handle->a.elements[idx], idx) == 0) ||
        (key != NULL) && (dlib_php_set_string((dlib_php_handle)&handle->a.elements[idx], key, strlen(key)))) {
            return dlib_php_set_string((dlib_php_handle)&handle->a.elements[idx+1], s, len);
    }

    return -1;
}

int dlib_php_set_array_element_null(dlib_php_handle handle, int idx, char *key)
{
    if ((handle == NULL) || (handle->t != DLIB_PHP_DATATYPE_ARRAY) ||
        (idx < 0) || (idx >= handle->a.count)) {
            return -1;
    }

    if ((key == NULL) && (dlib_php_set_integer((dlib_php_handle)&handle->a.elements[idx], idx) == 0) ||
        (key != NULL) && (dlib_php_set_string((dlib_php_handle)&handle->a.elements[idx], key, strlen(key)))) {
            return dlib_php_set_null((dlib_php_handle)&handle->a.elements[idx+1]);
    }

    return -1;
}

int dlib_php_set_array_element_array(dlib_php_handle handle, int idx, char *key, int count)
{
    if ((handle == NULL) || (handle->t != DLIB_PHP_DATATYPE_ARRAY) ||
        (idx < 0) || (idx >= handle->a.count)) {
            return -1;
    }

    if ((key == NULL) && (dlib_php_set_integer((dlib_php_handle)&handle->a.elements[idx], idx) == 0) ||
        (key != NULL) && (dlib_php_set_string((dlib_php_handle)&handle->a.elements[idx], key, strlen(key)))) {
            return dlib_php_set_array((dlib_php_handle)&handle->a.elements[idx+1], count);
    }

    return -1;
}

int dlib_php_set_array_element_object(dlib_php_handle handle, int idx, char *key, char *name, int len, int count)
{
    if ((handle == NULL) || (handle->t != DLIB_PHP_DATATYPE_ARRAY) ||
        (idx < 0) || (idx >= handle->a.count)) {
            return -1;
    }

    if ((key == NULL) && (dlib_php_set_integer((dlib_php_handle)&handle->a.elements[idx], idx) == 0) ||
        (key != NULL) && (dlib_php_set_string((dlib_php_handle)&handle->a.elements[idx], key, strlen(key)))) {
            return dlib_php_set_object((dlib_php_handle)&handle->a.elements[idx+1], name, len, count);
    }

    return -1;
}

int dlib_php_set_object(dlib_php_handle handle, char *name, int len, int count)
{
    char *str = NULL;
    dlib_php_element_t * elements = NULL;

    if ((handle == NULL) || (name ==  NULL) || (len <= 0) || (count <= 0)) {
        return -1;
    }

    if (handle->t != DLIB_PHP_DATATYPE_OBJECT) {
        str = (char*)dmalloc(sizeof(char) * (len + 1));
        if (str == NULL) {
            return -1;
        }

        elements = (dlib_php_element_t*)dmalloc(sizeof(dlib_php_element_t) * count * 2);
        if (elements == NULL) {
            return -1;
        }

        memset(elements, 0, sizeof(dlib_php_element_t) * count * 2);
        dlib_php_release_handle_internal(handle);
    }
    else {
        if (strlen(handle->o.name) < (unsigned int)len) {
            str = (char*)drealloc(handle->o.name, sizeof(char) * (len + 1));
            if (str == NULL) {
                return -1;
            }
        }

        if (handle->o.count < count) {
            elements = (dlib_php_element_t*)drealloc(handle->o.members, sizeof(dlib_php_element_t) * count * 2);
            if (elements == NULL) {
                return -1;
            }

            memset(&elements[2*handle->o.count], 0, sizeof(dlib_php_element_t) * 2 * (count - handle->o.count));
        }
    }

    memcpy(str, name, len);
    str[len] = 0;

    handle->o.name = str;
    handle->o.count = count;
    handle->o.members = elements;
    handle->t = DLIB_PHP_DATATYPE_OBJECT;

    return 0;
}

int dlib_php_set_object_member_bool(dlib_php_handle handle, int idx, char *name, int b)
{
    if ((handle == NULL) || (handle->t != DLIB_PHP_DATATYPE_OBJECT) ||
        (idx < 0) || (idx >= handle->o.count) || (name == NULL)) {
            return -1;
    }

    if (dlib_php_set_string((dlib_php_handle)&handle->o.members[idx], name, strlen(name)) == 0) {
        return dlib_php_set_bool((dlib_php_handle)&handle->o.members[idx+1], b);
    }

    return -1;
}

int dlib_php_set_object_member_integer(dlib_php_handle handle, int idx, char *name, int i)
{
    if ((handle == NULL) || (handle->t != DLIB_PHP_DATATYPE_OBJECT) ||
        (idx < 0) || (idx >= handle->o.count) || (name == NULL)) {
            return -1;
    }

    if (dlib_php_set_string((dlib_php_handle)&handle->o.members[idx], name, strlen(name)) == 0) {
        return dlib_php_set_integer((dlib_php_handle)&handle->o.members[idx+1], i);
    }

    return -1;
}

int dlib_php_set_object_member_double(dlib_php_handle handle, int idx, char *name, double d)
{
    if ((handle == NULL) || (handle->t != DLIB_PHP_DATATYPE_OBJECT) ||
        (idx < 0) || (idx >= handle->o.count) || (name == NULL)) {
            return -1;
    }

    if (dlib_php_set_string((dlib_php_handle)&handle->o.members[idx], name, strlen(name)) == 0) {
        return dlib_php_set_double((dlib_php_handle)&handle->o.members[idx+1], d);
    }

    return -1;
}

int dlib_php_set_object_member_string(dlib_php_handle handle, int idx, char *name, char *s, int len)
{
    if ((handle == NULL) || (handle->t != DLIB_PHP_DATATYPE_OBJECT) ||
        (idx < 0) || (idx >= handle->o.count) || (name == NULL)) {
            return -1;
    }

    if (dlib_php_set_string((dlib_php_handle)&handle->o.members[idx], name, strlen(name)) == 0) {
        return dlib_php_set_string((dlib_php_handle)&handle->o.members[idx+1], s, len);
    }

    return -1;
}

int dlib_php_set_object_member_null(dlib_php_handle handle, int idx, char *name)
{
    if ((handle == NULL) || (handle->t != DLIB_PHP_DATATYPE_OBJECT) ||
        (idx < 0) || (idx >= handle->o.count) || (name == NULL)) {
            return -1;
    }

    if (dlib_php_set_string((dlib_php_handle)&handle->o.members[idx], name, strlen(name)) == 0) {
        return dlib_php_set_null((dlib_php_handle)&handle->o.members[idx+1]);
    }

    return -1;
}

int dlib_php_set_object_member_array(dlib_php_handle handle, int idx, char *name, int count)
{
    if ((handle == NULL) || (handle->t != DLIB_PHP_DATATYPE_OBJECT) ||
        (idx < 0) || (idx >= handle->o.count) || (name == NULL)) {
            return -1;
    }

    if (dlib_php_set_string((dlib_php_handle)&handle->o.members[idx], name, strlen(name)) == 0) {
        return dlib_php_set_array((dlib_php_handle)&handle->o.members[idx+1], count);
    }

    return -1;
}

int dlib_php_set_object_member_object(dlib_php_handle handle, int idx, char *name, char *subname, int len, int count)
{
    if ((handle == NULL) || (handle->t != DLIB_PHP_DATATYPE_OBJECT) ||
        (idx < 0) || (idx >= handle->o.count) || (name == NULL)) {
            return -1;
    }

    if (dlib_php_set_string((dlib_php_handle)&handle->o.members[idx], name, strlen(name)) == 0) {
        return dlib_php_set_object((dlib_php_handle)&handle->o.members[idx+1], subname, len, count);
    }

    return -1;
}

dlib_php_element_t * dlib_php_get_element(dlib_php_handle handle)
{
    return handle;
}

int dlib_php_get_bool(dlib_php_handle handle)
{
    if ((handle != NULL) && (handle->t == DLIB_PHP_DATATYPE_BOOL)) {
        return handle->b;
    }
    return 0;
}

int dlib_php_get_integer(dlib_php_handle handle)
{
    if ((handle != NULL) && (handle->t == DLIB_PHP_DATATYPE_INT)) {
        return handle->i;
    }
    return 0;
}

double dlib_php_get_double(dlib_php_handle handle)
{
    if ((handle != NULL) && (handle->t == DLIB_PHP_DATATYPE_DOUBLE)) {
        return handle->d;
    }
    return 0;
}

char * dlib_php_get_string(dlib_php_handle handle)
{
    if ((handle != NULL) && (handle->t != DLIB_PHP_DATATYPE_STRING)) {
        return handle->s;
    }
    return NULL;
}

int dlib_php_get_null(dlib_php_handle handle)
{
    return 0;
}

int dlib_php_get_array_count(dlib_php_handle handle)
{
    if ((handle != NULL) && (handle->t == DLIB_PHP_DATATYPE_ARRAY)) {
        return handle->a.count;
    }
    return 0;
}

dlib_php_element_t * dlib_php_get_array_element_by_pos(dlib_php_handle handle, int idx)
{
    if ((handle != NULL) && (handle->t == DLIB_PHP_DATATYPE_ARRAY)) {
        return &handle->a.elements[2*idx+1];
    }
    return NULL;
}

dlib_php_element_t * dlib_php_get_array_element_by_key(dlib_php_handle handle, char *key)
{
    int i = 0;

    if ((handle != NULL) && (handle->t == DLIB_PHP_DATATYPE_ARRAY)) {
        for (i = 0; i < handle->a.count; i++) {
            switch (handle->a.elements[i].t) {
                case DLIB_PHP_DATATYPE_STRING:
                    if (strcasecmp(handle->a.elements[i].s, key) == 0) {
                        return &handle->a.elements[i+1];
                    }
                    break;
                case DLIB_PHP_DATATYPE_INT:
                    if (handle->a.elements[i].i == atoi(key)) {
                        return &handle->a.elements[i+1];
                    }
                    break;
            }
        }
    }
    return NULL;
}

char * dlib_php_get_object_name(dlib_php_handle handle)
{
    if ((handle != NULL) && (handle->t == DLIB_PHP_DATATYPE_OBJECT)) {
        return handle->o.name;
    }
    return NULL;
}

int dlib_php_get_object_count(dlib_php_handle handle)
{
    if ((handle != NULL) && (handle->t == DLIB_PHP_DATATYPE_OBJECT)) {
        return handle->o.count;
    }
    return 0;
}

dlib_php_element_t * dlib_php_get_object_member(dlib_php_handle handle, char *name)
{
    int i = 0;

    if ((handle != NULL) && (handle->t == DLIB_PHP_DATATYPE_OBJECT)) {
        for (i = 0; i < handle->o.count; i++) {
            if (handle->o.members[i].t == DLIB_PHP_DATATYPE_STRING) {
                if (strcasecmp(&handle->o.members[i].s[strlen(handle->o.name)+2], name) == 0) {
                    return &handle->o.members[i+1];
                }
            }
        }
    }
    return NULL;
}
